package com.piece.core.mongo.service;

import com.piece.core.framework.enums.OptionType;
import com.piece.core.framework.enums.StatusType;
import com.piece.core.framework.enums.sql.SqlKey;
import com.piece.core.framework.exception.ErrorCode;
import com.piece.core.framework.exception.FrameWorkException;
import com.piece.core.framework.util.cache.CacheFactory;
import com.piece.core.framework.util.collection.MapUtil;
import com.piece.core.framework.util.math.SequenceUtil;
import com.piece.core.framework.util.net.WeatherUtil;
import com.piece.core.framework.util.object.ClassUtil;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.framework.util.time.DateUtil;
import com.piece.core.log.client.LogFactory;
import com.piece.core.log.client.dto.MessageLogDTO;
import com.piece.core.log.support.LogUtil;
import com.piece.core.mongo.repository.MongoMessageRepository;
import com.piece.core.mongo.entity.MongoMessage;
import com.piece.core.framework.support.bootstrap.DataTable;
import com.piece.core.framework.support.bootstrap.Order;
import com.piece.core.framework.support.bootstrap.QueryTable;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.util.object.BeanUtil;
import com.piece.core.framework.util.async.AsyncManager;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Transactional
@Service(value = "mongoMessageService")
public class MongoMessageService {

    @Value("${spring.application.name:#{null}}")
    private String applicationName;

    @Autowired
    private MongoMessageRepository mongoMessageRepository;

    @Autowired
    private LogFactory logFactory;

    @Autowired
    private CacheFactory cacheFactory;

    public MongoMessage insert(MongoMessage message) {
        Date startTime = new Date();
        message.setId(SequenceUtil.uuid());
        if (OptionType.NOTICE.getCode().equals(message.getType())) {
            message.setReceiver("all");
        }
        message.setStartTime(startTime);
        message.setStatus(StatusType.UNREAD.getCode());
        try {
            message = mongoMessageRepository.save(message);
            if (null != message.getEndTime()) {
                // 设置消息删除时间
                dealOverdueMessage(message);
            }
            this.saveLog(message, StatusType.RUN.getCode(), "");
        } catch (Exception e) {
            this.saveLog(message, StatusType.FAIL.getCode(), e.getMessage());
            throw new FrameWorkException(ErrorCode.SAVE);
        }
        return message;
    }

    public MongoMessage insert(String title, String userId, String router, String query, String content) {
        MongoMessage message = new MongoMessage();
        message.setId(SequenceUtil.uuid());
        message.setTitle(title);
        message.setType(OptionType.MESSAGE.getCode());
        message.setReceiver(userId);
        message.setRouter(router);
        message.setQuery(query);
        message.setContent(content);
        return insert(message);
    }

    public void insertWithUsers(List<Long> receiverIds, String title, String messageType, String router, String query, String content, Date endTime) {
        MongoMessage message = new MongoMessage();
        for (long receiver : receiverIds) {
            message.setId(SequenceUtil.uuid());
            message.setTitle(title);
            message.setType(messageType);
            message.setReceiver(Convert.toStr(receiver));
            message.setRouter(router);
            message.setQuery(query);
            message.setContent(content);
            message.setStartTime(new Date());
            message.setEndTime(endTime);
            message.setStatus(StatusType.UNREAD.getCode());
            try {
                mongoMessageRepository.save(message);
                if (null != message.getEndTime()) {
                    // 设置消息删除时间
                    dealOverdueMessage(message);
                }
                this.saveLog(message, StatusType.RUN.getCode(), "");
            } catch (Exception e) {
                this.saveLog(message, StatusType.FAIL.getCode(), e.getMessage());
                throw new FrameWorkException(ErrorCode.SAVE);
            }
        }
    }

    /**
     * 记录发送日志
     */
    protected MessageLogDTO saveLog(MongoMessage message, String status, String desc) {
        MessageLogDTO logDTO = LogUtil.buildMessageLog(applicationName);

        if (null != logFactory && null != logFactory.getMessageLog()) {
            // 生成日志
            logDTO.setTitle(message.getTitle());
            logDTO.setType(message.getType());
            logDTO.setAddress(message.getReceiver());
            logDTO.setRouter(message.getRouter());
            logDTO.setContent(message.getContent());
            logDTO.setStartTime(message.getStartTime());
            logDTO.setEndTime(message.getEndTime());
            logDTO.setStatus(status);
            logDTO.setDesc(desc);
            logDTO = logFactory.getMessageLog().save(logDTO);
        }

        return logDTO;
    }

    /**
     * 过期任务自动删除
     */
    public void dealOverdueMessage(MongoMessage message) {
        long expired = message.getEndTime().getTime() - new Date().getTime();
        final MongoMessage message_ = message;
        AsyncManager.getInstance().execute(new TimerTask() {
            @Override
            public void run() {
                mongoMessageRepository.delete(message_);
            }
        }, expired, TimeUnit.MILLISECONDS);
    }

    public MongoMessage update(MongoMessage message) {
        return mongoMessageRepository.saveOrUpdate(message.getId(), message);
    }

    public void delete(String id) {
        MongoMessage message = new MongoMessage();
        message.setId(id);
        mongoMessageRepository.delete(message);
    }

    public MongoMessage findById(String id) {
        return mongoMessageRepository.get(id);
    }

    public DataTable findPage(QueryTable queryTable) {
        return mongoMessageRepository.findPage(queryTable);
    }

    /**
     * 查询所有有过期时间的通知公告
     *
     * @param type {@link OptionType}
     */
    public List<MongoMessage> findNoticesWithOverdueTime(String type) {
        QueryTable queryTable = new QueryTable();
        queryTable.setCurrentPage(1);
        queryTable.setStart(0);
        queryTable.setLength(0);
        queryTable = generateQueryParam(queryTable, type, null);
        if (null == queryTable) {
            return new ArrayList<>();
        }

        queryTable.getParams().put("Q_endTime_D_NNULL", null);
        DataTable dataTable = mongoMessageRepository.findPage(queryTable);
        return dataTable.getData();
    }

    public List<MongoMessage> findUserMessage(Object user, String type, int size) {
        QueryTable queryTable = new QueryTable();
        queryTable.setCurrentPage(1);
        queryTable.setStart(0);
        queryTable.setLength(size);
        queryTable = generateQueryParam(queryTable, type, user);
        if (null == queryTable) {
            return new ArrayList<>();
        }

        DataTable dataTable = mongoMessageRepository.findPage(queryTable);
        return dataTable.getData();
    }

    private QueryTable generateQueryParam(QueryTable queryTable, String type, Object user) {
        Map<String, Object> param = new LinkedHashMap<>();
        if (OptionType.NOTICE.getCode().equals(type) && null == user) {
            param.put("Q_receiver_S_EQ", "all");
        } else if (OptionType.NOTICE.getCode().equals(type) && null != user) {
            param.put("Q_receiver_S_EQ", Convert.toStr(BeanUtil.getFieldValue(user, "id")));
            param.put("Q_receiver_S_EQ_OR", "all");
        } else if (null != user) {
            param.put("Q_receiver_S_EQ", Convert.toStr(BeanUtil.getFieldValue(user, "id")));
        }

        if (null != type) {
            param.put("Q_type_S_EQ", type);
        }

        param.put("Q_status_S_EQ", StatusType.UNREAD.getCode());
        queryTable.setParams(param);
        List<Order> orders = new ArrayList<>();
        orders.add(new Order("startTime", SqlKey.ASC));
        queryTable.setOrder(orders);
        return queryTable;
    }

    public long countMessageByUser(Object user) {
        QueryTable queryTable = new QueryTable();
        queryTable.setCurrentPage(1);
        queryTable.setStart(0);
        queryTable.setLength(0);
        Map<String, Object> param = new HashMap<>();
        param.put("Q_receiver_S_EQ", Convert.toStr(BeanUtil.getFieldValue(user, "id")));
        param.put("Q_type_S_EQ", OptionType.MESSAGE.getCode());
        param.put("Q_status_S_EQ", StatusType.UNREAD.getCode());
        queryTable.setParams(param);
        return mongoMessageRepository.count(queryTable);
    }

    public long countNotifyByUser(Object user) {
        QueryTable queryTable = new QueryTable();
        queryTable.setCurrentPage(1);
        queryTable.setStart(0);
        queryTable.setLength(0);
        Map<String, Object> param = new LinkedHashMap<>();
        if (user != null) {
            param.put("Q_receiver_S_EQ", Convert.toStr(BeanUtil.getFieldValue(user, "id")));
            param.put("Q_receiver_S_EQ_OR", "all");
        } else {
            param.put("Q_receiver_S_EQ", "all");
        }
        param.put("Q_type_S_EQ", OptionType.NOTICE.getCode());
        param.put("Q_status_S_EQ", StatusType.UNREAD.getCode());
        queryTable.setParams(param);
        return mongoMessageRepository.count(queryTable);
    }

    public void refreshWeatherNotify() {
        MongoMessage weatherMessage = null;
        List<MongoMessage> messages = mongoMessageRepository.findByCondition(MapUtil.builder(new HashMap<String, Object>())
                .put("Q_type_S_EQ", OptionType.NOTICE.getCode()).put("Q_title_S_EQ", "天气预报").build());
        if (CollectionUtils.isNotEmpty(messages)) {
            weatherMessage = messages.get(0);
        } else {
            weatherMessage = new MongoMessage();
            weatherMessage.setType(OptionType.NOTICE.getCode());
            weatherMessage.setContent("天气预报");
        }
        Date startTime = new Date();
        weatherMessage.setTitle("天气预报");
        weatherMessage.setReceiver("all");
        weatherMessage.setStartTime(startTime);
        weatherMessage.setStatus(StatusType.UNREAD.getCode());
        try {
            String cityName = "北京";
            String cityCode = "101010100";
            Map weather = cacheFactory.get().getMap("WeatherInto-" + cityCode);
            if (ClassUtil.isEmpty(weather)) {
                weather = WeatherUtil.getInstance().getCityWeather(cityCode);
                // 第二天早4点过期
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(DateUtil.addDay(1));
                calendar.set(Calendar.HOUR_OF_DAY, 4);
                calendar.set(Calendar.MINUTE, 0);
                calendar.set(Calendar.SECOND, 0);
                long expire = calendar.getTimeInMillis() - new Date().getTime();
                cacheFactory.get().setMap("WeatherInto-" + cityCode, weather, expire/1000);
            }
            if (ClassUtil.isNotEmpty(weather)) {
                StringBuffer content = new StringBuffer("<span>" + cityName + "</span>");
                content.append("<span class='m-l-10'>" + weather.get("type") + "</span>");
                content.append("<span class='m-l-10'>" + weather.get("wendu") + "°C</span>");
                content.append("<span class='m-l-10'>" + weather.get("quality") + "</span>");
                weatherMessage.setContent(content.toString());
                if (StringUtil.isEmpty(weatherMessage.getId())) {
                    mongoMessageRepository.save(weatherMessage);
                } else {
                    mongoMessageRepository.updateById(weatherMessage.getId(), weatherMessage);
                }
            }
        } catch (Exception e) {
            throw new FrameWorkException(ErrorCode.SAVE);
        }
    }
}
